package com.koron.common.core.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 构建树结构
 * @param <T>   节点的对象类型
 * @param <K>   id、patentId的类型
 */
public class TreeUtils<T, K> {

    private final List<T> list;
    private final Function<? super T, ? extends K> idFunc;
    private final Function<? super T, ? extends K> pidFunc;
    private final TreeChildConsumer<T> setChildren;

    /**
     * 调用方法 TreeUtils<SysDept, String> tree = new TreeUtils<>(depts, SysDept::getDeptId, SysDept::getParentId, SysDept::setChildren);
     *
     * @param list        需要转换的list数据
     * @param idFunc      节点的id的get方法   支持lambda
     * @param pidFunc     节点的父id的get方法 支持lambda
     * @param setChildren 注入子节点的方法
     */
    public TreeUtils(List<T> list,
                     Function<? super T, ? extends K> idFunc,
                     Function<? super T, ? extends K> pidFunc,
                     TreeChildConsumer<T> setChildren) {
        this.list = list;
        this.idFunc = idFunc;
        this.pidFunc = pidFunc;
        this.setChildren = setChildren;
    }

    /**
     * 开始构建节点
     *
     * @return list 所有根节点的集合  (根节点可以是一个，也可以是多个)
     */
    public List<T> build() {
        List<T> data = this.list;
        if (data.isEmpty()) {
            return null;
        }
        List<T> rootList = new ArrayList<>();
        for (T root : data) {
            if (data.stream().noneMatch(item -> idFunc.apply(item).equals(pidFunc.apply(root)))) {
                rootList.add(buildChild(root));
            }
        }
        return rootList;
    }

    /**
     * 使用递归开始构建子节点
     *
     * @param parent 父节点
     * @return T 构建好当前节点的所有子节点
     */
    protected T buildChild(T parent) {
        List<T> children = new ArrayList<>();
        for (T t : list) {
            if (idFunc.apply(parent).equals(pidFunc.apply(t))) {
                children.add(buildChild(t));
            }
        }
        // 找到所有子节点后将其注入到父节点
        setChildren.accept(parent, children);
        return parent;
    }

    /**
     * 设置子节点
     *
     * @param <T>
     */
    @FunctionalInterface
    public interface TreeChildConsumer<T> {
        void accept(T t, List<T> tList);
    }

    /**
     * 获取子节点
     *
     * @param <T>
     */
    @FunctionalInterface
    public interface TreeChildGetConsumer<T> {
        void accept(T t, List<T> tList);
    }


    /**
     * 构造标识全路径
     * 构造名称全路径
     * @param childrenlist      要构造全路径的树，必须是树结构已完成的 集合
     * @param parent            父级节点 或者说 根节点，如果没有可以自己 new
     * @param fullCodeFunc      全路径标识的get方法   可以用 lambda表达式传参 必须是T类型
     * @param fullNameFunc      全路径名称的get方法   可以用 lambda表达式传参 必须是T类型
     * @param nameFunc          节点名称的get方法   可以用 lambda表达式传参 必须是T类型
     * @param fullCodeConnsu    全路径标识的set方法   BiConsumer<ClassifyDto, Object> fullCodeCons = (t, u) ->t.setFullPathCode(String.valueOf(u));
     * @param fullNameConnsu    全路径名称的set方法   BiConsumer<ClassifyDto, Object> fullNameCons = (t, u) ->t.setFullPathName(String.valueOf(u));
     * @param childrenfunc      子节点的get方法   可以用 lambda表达式传参 必须是T类型
     *
     * 调用示例：
     *     BiConsumer<ClassifyDto, Object> fullCodeCons = (t, u) ->t.setFullPathCode(String.valueOf(u));
     *     BiConsumer<ClassifyDto, Object> fullNameCons = (t, u) ->t.setFullPathName(String.valueOf(u));
     *     tree.buildFullPath(relist, relist.get(0),
     *             ClassifyDto::getFullPathCode,
     *             ClassifyDto::getFullPathName,
     *             ClassifyDto::getName,
     *             fullCodeCons,
     *             fullNameCons,
     *             ClassifyDto::getChildren);
     */
    public void buildFullPath(
            List<T> childrenlist,
            T parent,
            Function<? super T, ? extends K> fullCodeFunc,
            Function<? super T, ? extends K> fullNameFunc,
            Function<? super T, ? extends K> nameFunc,
            BiConsumer<? super T, Object> fullCodeConnsu,
            BiConsumer<? super T, Object> fullNameConnsu,
            Function<? super T, List<T>> childrenfunc) {
        for (T children : childrenlist) {
            if (childrenlist.stream().noneMatch(item -> idFunc.apply(parent).equals(pidFunc.apply(item)))) {
                fullCodeConnsu.accept(children, idFunc.apply(children));
                fullNameConnsu.accept(children, nameFunc.apply(children));
            } else {
                fullCodeConnsu.accept(children, StrUtil.join("/", fullCodeFunc.apply(parent), idFunc.apply(children)));
                fullNameConnsu.accept(children, StrUtil.join("/", fullNameFunc.apply(parent), nameFunc.apply(children)));
            }
            if (CollUtil.isNotEmpty(childrenfunc.apply(children))) {
                buildFullPath(
                        childrenfunc.apply(children),
                        children,
                        fullCodeFunc,
                        fullNameFunc,
                        nameFunc,
                        fullCodeConnsu,
                        fullNameConnsu,
                        childrenfunc);
            }
        }
    }

/**
 * 以下是不用函数式编程的递归实现
 */
//    public void rediu(List<ClassifyDto> children, ClassifyDto parent) {
//
//        for (ClassifyDto classifyDto : children) {
//            if (children.stream().noneMatch(item -> parent.getId().equals(item.getParentId()))) {
//                classifyDto.setFullPathCode(classifyDto.getId());
//                classifyDto.setFullPathName(classifyDto.getName());
//            } else{
//                classifyDto.setFullPathCode(StrUtil.join("/", parent.getFullPathCode(), classifyDto.getId()));
//                classifyDto.setFullPathName(StrUtil.join("/", parent.getFullPathName(), classifyDto.getName()));
//            }
//            if (CollUtil.isNotEmpty(classifyDto.getChildren())) {
//                rediu(classifyDto.getChildren(), classifyDto);
//            }
//        }
//    }


}
